"""
编译器访问者

实现AST的编译功能，将抽象的工作流转换为可执行的形式。
"""

import time
from typing import Any, Dict, List, Optional
from .base import BaseAstVisitor

# 前向声明，避免循环导入
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from ..ast.base import Ast, AstContext
    from ..ast.nodes import *


class CompilationResult:
    """编译结果"""
    
    def __init__(self):
        self.success: bool = True
        self.errors: List[str] = []
        self.warnings: List[str] = []
        self.compiled_ast: Optional['Ast'] = None
        self.compilation_time: float = 0.0
        self.metadata: Dict[str, Any] = {}
    
    def add_error(self, error: str) -> None:
        """添加错误"""
        self.errors.append(error)
        self.success = False
    
    def add_warning(self, warning: str) -> None:
        """添加警告"""
        self.warnings.append(warning)
    
    def has_errors(self) -> bool:
        """是否有错误"""
        return len(self.errors) > 0
    
    def has_warnings(self) -> bool:
        """是否有警告"""
        return len(self.warnings) > 0


class CompilerVisitor(BaseAstVisitor):
    """
    编译器访问者
    
    负责将AST编译为可执行的形式，包括：
    - 验证节点配置
    - 优化工作流结构
    - 生成执行计划
    - 资源预分配
    """
    
    def __init__(self):
        super().__init__()
        self.compilation_result = CompilationResult()
        self.optimization_enabled = True
        self.strict_mode = False
    
    def compile(self, ast: 'Ast', context: 'AstContext') -> CompilationResult:
        """编译AST"""
        start_time = time.time()
        
        try:
            # 重置编译结果
            self.compilation_result = CompilationResult()
            
            # 执行编译
            compiled_ast = self.visit(ast, context)
            
            # 设置编译结果
            self.compilation_result.compiled_ast = compiled_ast
            self.compilation_result.compilation_time = time.time() - start_time
            
            # 如果有错误且是严格模式，标记为失败
            if self.strict_mode and self.compilation_result.has_errors():
                self.compilation_result.success = False
            
        except Exception as e:
            self.compilation_result.add_error(f"编译异常: {e}")
            self.compilation_result.compilation_time = time.time() - start_time
        
        return self.compilation_result
    
    def visit(self, ast: 'Ast', context: 'AstContext') -> 'Ast':
        """重写visit方法，添加编译逻辑"""
        # 验证节点
        self._validate_node(ast)
        
        # 预处理节点
        ast = self._preprocess_node(ast, context)
        
        # 调用父类方法
        result = super().visit(ast, context)
        
        # 后处理节点
        result = self._postprocess_node(result, context)
        
        return result
    
    def _validate_node(self, ast: 'Ast') -> None:
        """验证节点配置"""
        errors = ast.validate()
        for error in errors:
            self.compilation_result.add_error(f"节点 {ast.name} 验证失败: {error}")
    
    def _preprocess_node(self, ast: 'Ast', context: 'AstContext') -> 'Ast':
        """预处理节点"""
        # 克隆节点以避免修改原始AST
        processed_ast = ast.clone()
        
        # 设置默认值
        self._set_default_values(processed_ast, context)
        
        # 解析变量引用
        self._resolve_variables(processed_ast, context)
        
        return processed_ast
    
    def _postprocess_node(self, ast: 'Ast', context: 'AstContext') -> 'Ast':
        """后处理节点"""
        # 优化节点
        if self.optimization_enabled:
            ast = self._optimize_node(ast, context)
        
        return ast
    
    def _set_default_values(self, ast: 'Ast', context: 'AstContext') -> None:
        """设置默认值"""
        # 为不同类型的节点设置默认值
        if hasattr(ast, 'input_path') and not ast.input_path:
            # 从上下文获取默认输入路径
            ast.input_path = context.get_variable('default_input_path')
        
        if hasattr(ast, 'output_path') and not ast.output_path:
            # 从上下文获取默认输出路径
            ast.output_path = context.get_variable('default_output_path')
    
    def _resolve_variables(self, ast: 'Ast', context: 'AstContext') -> None:
        """解析变量引用"""
        # 遍历节点的所有属性，解析变量引用
        for attr_name in dir(ast):
            if attr_name.startswith('_'):
                continue
            
            attr_value = getattr(ast, attr_name, None)
            if isinstance(attr_value, str) and attr_value.startswith('$'):
                # 这是一个变量引用
                var_name = attr_value[1:]
                resolved_value = context.get_variable(var_name)
                if resolved_value is not None:
                    setattr(ast, attr_name, resolved_value)
                else:
                    self.compilation_result.add_warning(f"未找到变量: {var_name}")
    
    def _optimize_node(self, ast: 'Ast', context: 'AstContext') -> 'Ast':
        """优化节点"""
        # 这里可以实现各种优化策略
        # 例如：合并相邻的相同类型节点、消除无用节点等
        return ast
    
    # ========================================================================
    # 具体节点的编译实现
    # ========================================================================
    
    def visit_sequence(self, ast: 'Sequence', context: 'AstContext') -> 'Sequence':
        """编译顺序执行节点"""
        compiled_children = []
        
        for child in ast.children:
            compiled_child = self.visit(child, context)
            compiled_children.append(compiled_child)
        
        # 创建编译后的节点
        compiled_ast = ast.clone()
        compiled_ast.children = compiled_children
        
        return compiled_ast
    
    def visit_parallel(self, ast: 'Parallel', context: 'AstContext') -> 'Parallel':
        """编译并行执行节点"""
        # 检查并行度设置
        if ast.max_workers <= 0:
            self.compilation_result.add_error("并行节点的最大工作线程数必须大于0")
            ast.max_workers = 1
        
        # 检查系统资源
        import os
        cpu_count = os.cpu_count() or 1
        if ast.max_workers > cpu_count * 2:
            self.compilation_result.add_warning(
                f"并行度 {ast.max_workers} 超过推荐值 {cpu_count * 2}"
            )
        
        compiled_children = []
        for child in ast.children:
            compiled_child = self.visit(child, context)
            compiled_children.append(compiled_child)
        
        compiled_ast = ast.clone()
        compiled_ast.children = compiled_children
        
        return compiled_ast
    
    def visit_conditional(self, ast: 'Conditional', context: 'AstContext') -> 'Conditional':
        """编译条件执行节点"""
        # 验证条件表达式
        if not ast.condition:
            self.compilation_result.add_error("条件节点必须指定条件表达式")
        
        # 编译分支
        compiled_true_branch = None
        compiled_false_branch = None
        
        if ast.true_branch:
            compiled_true_branch = self.visit(ast.true_branch, context)
        
        if ast.false_branch:
            compiled_false_branch = self.visit(ast.false_branch, context)
        
        compiled_ast = ast.clone()
        compiled_ast.true_branch = compiled_true_branch
        compiled_ast.false_branch = compiled_false_branch
        
        return compiled_ast
    
    def visit_loop(self, ast: 'Loop', context: 'AstContext') -> 'Loop':
        """编译循环执行节点"""
        # 检查循环配置
        if not ast.condition and not ast.max_iterations:
            self.compilation_result.add_warning("循环节点没有终止条件，可能导致无限循环")
        
        if ast.max_iterations and ast.max_iterations > 1000:
            self.compilation_result.add_warning(
                f"循环次数 {ast.max_iterations} 过大，可能影响性能"
            )
        
        compiled_children = []
        for child in ast.children:
            compiled_child = self.visit(child, context)
            compiled_children.append(compiled_child)
        
        compiled_ast = ast.clone()
        compiled_ast.children = compiled_children
        
        return compiled_ast
    
    def visit_ffmpeg_slice(self, ast: 'FfmpegSlice', context: 'AstContext') -> 'FfmpegSlice':
        """编译FFmpeg切片节点"""
        # 检查FFmpeg可用性
        import shutil
        if not shutil.which('ffmpeg'):
            self.compilation_result.add_error("未找到FFmpeg可执行文件")
        
        # 验证参数
        if ast.max_duration <= ast.min_duration:
            self.compilation_result.add_error("最大时长必须大于最小时长")
        
        # 设置默认输出路径
        if not ast.output_path:
            ast.output_path = context.get_variable('temp_path', './temp')
        
        return ast.clone()
    
    def visit_extract_metadata(self, ast: 'ExtractMetadata', context: 'AstContext') -> 'ExtractMetadata':
        """编译元数据提取节点"""
        # 验证缩略图配置
        if ast.include_thumbnails and ast.thumbnail_count <= 0:
            self.compilation_result.add_error("缩略图数量必须大于0")
        
        return ast.clone()
    
    def visit_scan_directory(self, ast: 'ScanDirectory', context: 'AstContext') -> 'ScanDirectory':
        """编译目录扫描节点"""
        # 检查路径存在性
        import os
        if not os.path.exists(ast.path):
            self.compilation_result.add_error(f"扫描路径不存在: {ast.path}")
        
        # 验证文件扩展名
        if not ast.extensions:
            self.compilation_result.add_warning("未指定文件扩展名，将扫描所有文件")
        
        return ast.clone()
    
    def visit_gemini_classify(self, ast: 'GeminiClassify', context: 'AstContext') -> 'GeminiClassify':
        """编译Gemini分类节点"""
        # 检查API密钥
        api_key = context.get_variable('GEMINI_API_KEY')
        if not api_key:
            self.compilation_result.add_error("未设置Gemini API密钥")
        
        # 验证分类类别
        if not ast.categories:
            self.compilation_result.add_error("必须指定分类类别")
        
        return ast.clone()
    
    def visit_output_draft(self, ast: 'OutputDraft', context: 'AstContext') -> 'OutputDraft':
        """编译输出草稿节点"""
        # 设置默认项目名称
        if not ast.project_name:
            ast.project_name = f"MixVideo_Project_{int(time.time())}"
        
        return ast.clone()
    
    def visit_organize_files(self, ast: 'OrganizeFiles', context: 'AstContext') -> 'OrganizeFiles':
        """编译文件整理节点"""
        # 验证路径结构模板
        if '{category}' not in ast.structure:
            self.compilation_result.add_warning("文件结构模板中缺少 {category} 占位符")
        
        return ast.clone()
    
    def visit_generate_report(self, ast: 'GenerateReport', context: 'AstContext') -> 'GenerateReport':
        """编译报告生成节点"""
        # 设置默认输出路径
        if not ast.output_path:
            ast.output_path = f"./reports/report_{int(time.time())}.{ast.format}"
        
        return ast.clone()
    
    def visit_file_operation(self, ast: 'FileOperation', context: 'AstContext') -> 'FileOperation':
        """编译文件操作节点"""
        # 验证操作类型和路径
        if ast.operation in ['copy', 'move', 'rename'] and not ast.target_path:
            self.compilation_result.add_error(f"{ast.operation} 操作需要指定目标路径")
        
        return ast.clone()
    
    def visit_data_transform(self, ast: 'DataTransform', context: 'AstContext') -> 'DataTransform':
        """编译数据转换节点"""
        # 验证转换类型
        if not ast.transform_type:
            self.compilation_result.add_error("必须指定数据转换类型")
        
        return ast.clone()
